
package com.thinkgem.jeesite.common.pojo.auth.data.ic;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.log4j.Logger;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;

import com.thinkgem.jeesite.common.pojo.auth.data.type.HandlerScope;
import com.thinkgem.jeesite.common.pojo.runtime.StringUtils;

/**
 * handler注解构造工厂实例
 * @author WMS
 * root in:wms.studio:MulitmediaDesktop
 * root git:https://github.com/wu560130911/MultimediaDesktop
 */
public class HandlerAnnotationFactoryBean {
	private static final Logger log = Logger
	        .getLogger(HandlerAnnotationFactoryBean.class);

	private final String check_packages = "com.thinkgem.jeesite.common.pojo.auth.data.annotation.Handler";

	private static final String RESOURCE_PATTERN = "/**/*.class";

	private final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

	private final List<String> packagesList = new LinkedList<String>();

	private final List<TypeFilter> typeFilters = new LinkedList<TypeFilter>();

	private static final ConcurrentHashMap<String, ConcurrentHashMap<HandlerScope, LinkedList<String>>> handlersBeanNameMap = 
			new ConcurrentHashMap<String, ConcurrentHashMap<HandlerScope, LinkedList<String>>>();

	/**
	 * 构造函数
	 * 
	 * @param packagesToScan
	 *            指定哪些包需要被扫描,支持多个包"package.a,package.b"并对每个包都会递归搜索
	 * @param annotationFilter
	 *            指定扫描包中含有特定注解标记的bean,支持多个注解
	 */
	@SafeVarargs
	public HandlerAnnotationFactoryBean(String[] packagesToScan,
	        Class<? extends Annotation>... annotationFilter) {
		if (packagesToScan != null) { //把包路径取出,添加到包列表中
			for (String packagePath : packagesToScan) {
				this.packagesList.add(packagePath);
			}
		}
		if (annotationFilter != null) {   //把注解项取出,添加到注解类型过滤列表中
			for (Class<? extends Annotation> annotation : annotationFilter) {
				typeFilters.add(new AnnotationTypeFilter(annotation, false));//标记注解类型中不包含'元注解'
			}
		}
	}

	/**
	 * 将符合条件的Bean以Class集合的形式返回
	 * 
	 * @return
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public void init() {
		log.info("业务Handler注解工厂类，扫描系统中所有的业务处理类");
		beforeHandlerResource(handlersBeanNameMap);
	}

	/**
	 * 预处理资源
	 * @param handlersMap
	 */
	private void beforeHandlerResource(
	        ConcurrentHashMap<String, ConcurrentHashMap<HandlerScope, LinkedList<String>>> handlersMap) {
		try {

			if (!this.packagesList.isEmpty()) {
				for (String pkg : this.packagesList) { //包列表不为空时,取出包路径,进行比对
				    //patten=classpath*:{$pkg}/**/*.class,其中pkg的格式为/com/thinkgem/.../classname
					String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
					        + ClassUtils.convertClassNameToResourcePath(pkg)
					        + RESOURCE_PATTERN;
					//从资源模式扫描器中获取满足pattern匹配的class资源.
					Resource[] resources = 
							this.resourcePatternResolver.getResources(pattern);
					//使用资源模式扫描器构造元数据读取工厂
					MetadataReaderFactory readerFactory = 
							new CachingMetadataReaderFactory(this.resourcePatternResolver);

					//将资源放入handler容器进行绑定和过滤处理
					onHandlerResource(resources, readerFactory, handlersMap);

				}
			}
		} catch (IOException e) {
			log.fatal("扫描业务处理异常", e);
		}
	}

	/**
	 * handler容器的绑定和过滤处理
	 * @param resources        class资源
	 * @param readerFactory    元数据读取工厂
	 * @param handlersMap      handler容器
	 * @throws IOException
	 */
	private void onHandlerResource(
	        Resource[] resources,
	        MetadataReaderFactory readerFactory,
	        ConcurrentHashMap<String, ConcurrentHashMap<HandlerScope, LinkedList<String>>> handlersMap)
	        throws IOException {

	    //classpath资源或元数据读取工厂为空则直接返回
		if (resources == null || readerFactory == null) {
			return;
		}

		for (Resource resource : resources) {
			if (resource.isReadable()) { //检索资源列表,如果是可读资源,则使用资源构造一个元数据读取对象
				MetadataReader reader = readerFactory.getMetadataReader(resource);
				// 这儿可以抽象出来，但是因为本项目只需要一个注解扫描
				if (matchesEntityTypeFilter(reader, readerFactory)) {
					log.debug(ClassUtils.convertResourcePathToClassName(resource.getDescription()));
					//获取指定注解中的属性map(当前为handler)
					Map<String, Object> map = 
							reader.getAnnotationMetadata().getAnnotationAttributes(this.check_packages);
					if (map != null) { //读取handler注解的属性值
						String handlerName = (String) map.get("handlerName");
						String beanName = (String) map.get("beanName");
						HandlerScope scope = (HandlerScope) map.get("scope");
						if (scope == HandlerScope.None) { //如果scope是none,则直接跳过不处理
							continue;
						}

						//加入handler流程处理
						addHandler(handlerName, beanName, scope, handlersMap);
					}
				}
			}
		}
	}

    /**
     * 检查当前扫描到的Bean含有任何一个指定的注解标记
     * 注意当前注解已经不包含任何元注解对应的class资源
     * @param reader
     * @param readerFactory
     * @return
     * @throws IOException
     */
    private boolean matchesEntityTypeFilter(MetadataReader reader,
            MetadataReaderFactory readerFactory) throws IOException {
        if (!this.typeFilters.isEmpty()) {
            for (TypeFilter filter : this.typeFilters) {
                if (filter.match(reader, readerFactory)) {
                    return true;
                }
            }
        }
        return false;
    }

	/**
	 * handler流程处理
	 * @param handlerName  待处理的handler名称
	 * @param beanName     待处理的bean名称
	 * @param scope        处理链范围
	 * @param handlersMap  待填充的处理链容器
	 */
	private void addHandler(
	        String handlerName,
	        String beanName,
	        HandlerScope scope,
	        ConcurrentHashMap<String, ConcurrentHashMap<HandlerScope, LinkedList<String>>> handlersMap) {

	    //如果bean名称和handler名称是空则直接跳过处理
		if (StringUtils.isBlank(beanName) || StringUtils.isBlank(handlerName)) {
			return;
		}

		//从处理链容器中根据handler名称取出handler处理链
		ConcurrentHashMap<HandlerScope, LinkedList<String>> handlerNameHandler = 
		        handlersMap.get(handlerName);

		//如果handler处理链为空,则根据handler名称直接构造新的放入处理链中
		if (handlerNameHandler == null) {
			handlerNameHandler = new ConcurrentHashMap<HandlerScope, LinkedList<String>>();
			handlersMap.put(handlerName, handlerNameHandler);
		}

		//从handler处理链中根据数据范围取出bean处理链
		LinkedList<String> handlerApiBeans = handlerNameHandler.get(scope);

		//如果bean处理链为空,则根据数据范围直接构造新的放入处理链中
		if (handlerApiBeans == null) {
			handlerApiBeans = new LinkedList<String>();
			handlerNameHandler.put(scope, handlerApiBeans);
		}

		//在bean处理链中添加bean名称
		handlerApiBeans.add(beanName);
	}

	/**
	 * 根据handler名称从处理链容器中取出before范围的handler链
	 * @param handlerName  handler名称
	 * @return handler链
	 */
	public List<String> getBeforeHandlers(String handlerName) {

		return getHandlers(handlerName, HandlerScope.Before);
	}

	/**
	 * 根据handler名称从处理链容器中取出after范围的handler链
	 * @param handlerName handler名称
	 * @return handler链
	 */
	public List<String> getAfterHandlers(String handlerName) {
		return getHandlers(handlerName, HandlerScope.After);
	}

	/**
	 * 根据handler名称和数据范围从处理链容器中取出handler链对应的bean处理链
	 * @param handlerName handler名称
	 * @param scope 数据范围
	 * @return handler链
	 */
	public List<String> getHandlers(String handlerName, HandlerScope scope) {

	    //如果handler名称为空或者数据范围为空或数据范围为none,则直接返回空链
		if (StringUtils.isBlank(handlerName) || scope == null
		        || HandlerScope.None.equals(scope)) {
			return Collections.emptyList();
		}

		//从处理链容器中取出与handler名称相同的handler链
		ConcurrentHashMap<HandlerScope, LinkedList<String>> handlerNameHandler = 
		        handlersBeanNameMap.get(handlerName);
		if (handlerNameHandler == null) { //如果处理链容器中没有与handler名称相同的handler链,则直接返回空链
			return Collections.emptyList();
		}

		//从handler链中根据数据范围取出bean处理链
		LinkedList<String> handlerApis = handlerNameHandler.get(scope);

		if (handlerApis == null) { //如果handler链中没有满足数据范围的bean处理链,则直接返回空链
			return Collections.emptyList();
		}

		//返回bean处理链,同时设置为只读(只能由自身进行修改)
		return Collections.unmodifiableList(handlerApis);
	}

	/**
	 * 同步刷新处理链容器(class变更或定时器)
	 * 
	 */
	public synchronized void refresh() {
		final ConcurrentHashMap<String, ConcurrentHashMap<HandlerScope, LinkedList<String>>> tempHandlersMap = 
		        new ConcurrentHashMap<String, ConcurrentHashMap<HandlerScope, LinkedList<String>>>();//创建静态临时处理链容器
		beforeHandlerResource(tempHandlersMap);//预处理临时处理链容器
		handlersBeanNameMap.clear();//清空全局处理链容器
		handlersBeanNameMap.putAll(tempHandlersMap);//将临时处理链容器放入全局
	}
}
